﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Noris.Schedule.Support.Services;

namespace Noris.Schedule.Support.Data
{
	#region CLASS GridDataElementCls : jeden prvek grafu, jeho logická data
	/// <summary>
	/// Jedna položka, která popisuje jednu položku grafu.
	/// Obsahuje informaci o parent GID = řádek, do něhož patří, obsahuje svůj GID = specifikuje svoji třídu, 
	/// obsahuje časový úsek v rámci grafu a další informace o způsobu využití uvedeného času.
	/// Neobsahuje nic grafického ve smyslu rozměrů, pozice, barvy, ikony atd.
	/// </summary>
	public class GridDataElementCls : Noris.Schedule.Support.Services.IDataElement
	{
		#region PROPERTY
		/// <summary> { get; }
		/// Globální identifikátor záznamu = tohoto elementu
		/// </summary>
		public GID GId { get { return _GId; } }
		/// <summary> { get; set; }
		/// Globální identifikátor rodičovského záznamu = řádek, v kterém se element nachází.
		/// Pokud je toto čas operace, pak parentem je operace.
		/// Pokud tento element je čas stavu kapacit, jde o kapacitní jednotku.
		/// Pokud tento element představuje část práce z konkrétní operace na konkrétní kapacitě, pak bude zobrazen na dané kapacitě.
		/// Jde o aktuální údaj, který může obsahovat editovanou hodnotu.
		/// </summary>
		public GID RowGId { get { return _RowGId; } set { _RowGId = value; } }
		/// <summary> { get; }
		/// Globální identifikátor rodičovského záznamu = řádek, v kterém se element nachází.
		/// Jde o původní údaj, načtený z databáze. Grafický element jej nemůže změnit.
		/// </summary>
		public GID OriginalRowGId { get { return _OriginalRowGId; } }
		/// <summary> { get; }
		/// Globální identifikátor pracovní jednotky: jedna pracovní jednotka může být zobrazena ve více grafech (různé typy grafů),
		/// a v rámci jednoho grafu může být jedna práce zobrazena ve více časových elementech (časově roztrhaná práce), ale přesto je to jeden pracovní úkol.
		/// </summary>
		public GID WorkGId { get { return _WorkGId; } }
		/// <summary>
		/// Globální identifikátor nadřízené pracovní jednotky: původce tohoto pracovního úseku.
		/// Pokud tento element je práce na operaci, pak parentem je výrobní příkaz/dílec.
		/// Pokud tento element je dílec, pak parentem může být zakázka, nebo nic.
		/// Pokud tento element je stav kapacit, pak parenta nemá: do parenta se promítají změny v čase této operace, a stav kapacit nelze změnit přetáhnutím.
		/// </summary>
		public GID ParentWorkGId { get { return _ParentWorkGId; } set { _ParentWorkGId = value; } }
		/// <summary>
		/// Globální identifikátor podřízených záznamů. Obsahuje WorkGId záznamů elementů, které jsou podřízené tomuto elementu.
		/// Lze tak sestavit mapu (strom) 
		/// </summary>
		public GID[] FirstChildWorkGIds { get { return _FirstChildWorkGIds; } set { _FirstChildWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Globální identifikátor WorkGId pracovních úkolů, které předcházejí výkonu práce na elementech this.WorkGId
		/// </summary>
		public GID[] PreviousWorkGIds { get { return _PreviousWorkGIds; } set { _PreviousWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Globální identifikátor WorkGId pracovních úkolů, které následují po výkonu práce na elementech this.WorkGId
		/// </summary>
		public GID[] NextWorkGIds { get { return _NextWorkGIds; } set { _NextWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Časový interval tohoto elementu (od kdy do kdy trvá).
		/// Jde o aktuální údaj, který může obsahovat editovanou hodnotu.
		/// </summary>
		public TimeRange TimeRange { get { return _TimeRange; } set { _TimeRange = value; } }
		/// <summary> { get; }
		/// Časový interval tohoto elementu (od kdy do kdy trvá).
		/// Jde o původní údaj, načtený z databáze. Grafický element jej nemůže změnit.
		/// </summary>
		public TimeRange OriginalTimeRange { get { return _OriginalTimeRange; } }
		/// <summary>
		/// Zlomky elementu, převezme si je vhodný painter
		/// </summary>
		public float[] Fragments { get { return this._Fragments; } set { this._Fragments = value; } }
		/// <summary> { get; }
		/// Příslušnost data do vrstvy.
		/// Odlišuje vrstvu podkladovou (typicky zobrazení směny) a vrstvu datovou (aktivita v čase, která může být delší / kratší než směna).
		/// </summary>
		public GraphElementLayerType ElementLayer { get { return _ElementLayer; } }
		/// <summary> { get; }
		/// Definuje režim, jakým lze touto položkou pohybovat ( Move a Resize )
		/// a jaký vliv má tento pohyb na okolní položky ( nadřízené a sousední ).
		/// Tato proměnná má význam pouze pro vrstvu (ElementLayer == GraphElementLayerType.ItemLayer).
		/// </summary>
		public DataElementEditingMode ElementEditingMode { get { return _ElementEditingMode; } }
		/// <summary>
		/// Tvar elementu.
		/// </summary>
		public GraphElementShapeType ElementShapeType { get { return GraphElementShapeType.Rectangle; } }
		/// <summary>
		/// Barva pozadí elementu
		/// </summary>
		public Color BackColor { get { return this._BackColor; } }
		/// <summary>
		/// Barva písma v elementu
		/// </summary>
		public Color FontColor { get { return this._FontColor; } }
		/// <summary> { get; }
		/// Specifikuje druh obsahu, ovlivňuje způsob zobrazování (barvu, vzhled).
		/// Datová vrstva zde může odlišovat různé typy směn, různé režimy práce, různé úrovně hierarchie zakázky, různé zobrazované třídy.
		/// U grafu typu RowGraphMode.Gantt ovlivní barvu políčka, vyjadřuje druh činnosti.
		/// U grafu typu RowGraphMode.CapacityUtilization ovlivní barvu políčka, vyjadřuje druh směny.
		/// U grafu typu RowGraphMode.TaskCapacityLink ovlivňuje barvu políčka směny (pokud je this.ElementLayer == SubLayer), nebo práce (pokud je this.ElementLayer == ItemLayer)
		/// Konkrétní barvu generuje metoda IDataSource.GetColorForElement()
		/// </summary>
		public GraphElementVisualType ElementVisualType { get { return _ElementVisualType; } }
        /// <summary>
        /// Ikona vykreslovaná v elementu.
        /// Ikona se kreslí tak, že její střed leží na středu času Begin.
        /// Může být null.
        /// </summary>
        public Image Icon { get { return _Icon; } set { _Icon = value; } }
        /// <summary> { get; }
		/// Splnění úkolu, typicky u operací, které jsou částečně nebo zcela odvedeny.
		/// Zohledňuje se u grafu typu Gantt. (Typ grafu určuje RowGId (typ řádku).)
		/// Smysluplné hodnoty: 0 (= ještě se ani nezačalo) až 1 (= vše je hotovo). Hodnoty mimo rozsah 0 až 1 mají význam 0 nebo 1.
		/// </summary>
		public float CompletionRatio { get { return _CompletionRatio; } set { _CompletionRatio = value; } }
		/// <summary> { get; }
		/// Využití stavu kapacity v poměrné hodnotě (1.00 = 100%). Záporné hodnoty mají význam jako 0. 
		/// Hodnoty nad 1.00 značí přečerpání kapacit a zobrazují se.
		/// </summary>
		public float UtilizationRatio { get { return _UtilizationRatio; } set { _UtilizationRatio = value; } }
		/// <summary>
		/// Poměrné využití výrobní linky pro tuto operaci (podílové využití)
		/// </summary>
		public float UseRatio { get { return _UseRatio; } set { _UseRatio = value; } }
		/// <summary> { get; set; }
		/// Číslo linkovací skupiny, typicky číslo subjektu ve vztahu "lcs.plan_unit_c_task.link".
		/// Hodnota 0 = není link, tento záznam není nijak spojen s jinými záznamy.
		/// </summary>
		public int LinkGroup { get { return _LinkGroup; } set { _LinkGroup = value; } }
		/// <summary>
		/// Vrátí text, který se zobrazuje v ToolTipu.
		/// Může vrátit null, pak se za tento objekt nic nezobrazí.
		/// Může vrátit text, obsahující CrLf, pak bude text odřádkován.
		/// </summary>
		public string ToolTipInfo(IDataRow rowData)
		{
			// Podle typu grafu:
			string timeRange = "Čas od: " + this.TimeRange.Begin.ToString() + "  do: " + this.TimeRange.End.ToString();
			string eol = "\r\n";
			string result = null;
			double percent;
			if (this.ElementLayer == GraphElementLayerType.TempLayer)
				result = timeRange;
			else
			{
				switch (rowData.RowGraph)
				{
					case RowGraphMode.CapacityUtilization:
						percent = Math.Round(this.UtilizationRatio * 100F, 1);
						result = timeRange + eol + "Kapacita využita na " + percent.ToString() + "%";
						break;
					case RowGraphMode.Gantt:
						result = timeRange;
						percent = Math.Round(this.CompletionRatio * 100F, 1);
						if (percent > 0F)
							result += eol + "Dokončeno " + percent.ToString() + "%";
						break;
					case RowGraphMode.TaskCapacityLink:
						result = timeRange;
						break;
				}
			}
			// TextNote:
			string eols = "\r\n      ";
			if (this.TextNote != null)
				result += eols + this.TextNote.Replace("\r\n", eols);

			return result;
		}
		/// <summary>
		/// Vrátí text, který se zobrazuje v obdélníku elementu při jeho "zaměření", například při editaci.
		/// Jde o krátkou, zásadně jednořádkovou informaci, jejíž délka (počet znaků) je omezena 
		/// fyzickou velikostí elementu při aktuálním rozlišení.
		/// </summary>
		/// <param name="rowData">Data o řádku, v němž se element nachází. Řádek může obsahovat některé potřebné informace.</param>
		/// <param name="charCount">Nejvyšší počet aktuálně zobrazitelných znaků. 
		/// Pokud bude vrácený text obsahovat více znaků, budou pravděpodobně oříznuty. 
		/// Pravděpodobnost je způsobena proporcionálním písmem. 
		/// Daný počet znaků je vypočten na maximální šířku znaku, jde tedy o garantovaný zobrazitelný počet. 
		/// Pokud bude vrácen text delší, ale složený z užších znaků, možná se podaří je zobrazit korektně. 
		/// Jsme v grafickém prostředí Windows...</param>
		/// <returns></returns>
		public string Caption(IDataRow rowData, float charCount)
		{
			string result = rowData.Name;

			if (this.TextCaptions == null) return result;

			foreach (string text in this.TextCaptions)
			{
				if (text == null) continue;
				string caption = text.Trim();

				if (caption.Length < charCount)
					result = caption;
				else
					break;
			}

			return result;
		}
        /// <summary>
        /// Režim viditelnosti elementu. Výchozí je Default (0): elementy Gantt jsou viditelné vždy, elementy TaskLink jsou viditelné jen pokud jsou nějak aktivní (myší, výběrem).
        /// </summary>
        public CaptionVisibilityMode CaptionVisibility { get { return this._CaptionVisibility; } set { this._CaptionVisibility = value; } }
		#endregion
		#region KONSTRUKTORY A VIZUALIZACE
		public GridDataElementCls(GID gId, GID rowGId, GID workGId, TimeRange timeRange)
		{
			this._GId = gId;
			this._RowGId = rowGId;
			this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = DataElementEditingMode.DefaultEditingState;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = 0F;

		}
		public GridDataElementCls(GID gId, GID rowGId, GID workGId, TimeRange timeRange, DataElementEditingMode moveMode)
		{
			this._GId = gId;
			this._RowGId = rowGId;
			this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = moveMode;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = 0F;

		}
		public GridDataElementCls(GID gId, GID rowGId, GID workGId, TimeRange timeRange, float completionRatio)
		{
			this._GId = gId;
			this._RowGId = rowGId;
			this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = DataElementEditingMode.DefaultEditingState;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = completionRatio;
		}
		/// <summary>
		/// Vytvoří a vrátí Temporary element na základě požadavku na načítání dat.
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		public static GridDataElementCls CreateTempElement(DataSourceRequestReadElements request)
		{
			GridDataElementCls element = new GridDataElementCls(request.TempGId, request.RequestRowGId, request.RequestedWorkGId, request.RequestedTimeRange);
			element.SetElementProperty(GraphElementLayerType.TempLayer, DataElementEditingMode.DeadElement, GraphElementVisualType.Type1);
			return element;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Element GID=" + this.GId.ToString() + "; Parent=" + this.RowGId.ToString() + "; Time=" + this.TimeRange.ToString() + "}";
		}
		#endregion
		#region FIELDS
		private GID _GId;
		private GID _RowGId;
		private GID _OriginalRowGId;
		private GID _WorkGId;
		private GID _ParentWorkGId;
		private GID[] _FirstChildWorkGIds;
		private GID[] _PreviousWorkGIds;
		private GID[] _NextWorkGIds;
		private TimeRange _TimeRange;
		private TimeRange _OriginalTimeRange;
		private float[] _Fragments;
		private GraphElementLayerType _ElementLayer;
		private DataElementEditingMode _ElementEditingMode;
		private Color _BackColor;
		private Color _FontColor;
		private GraphElementVisualType _ElementVisualType;
        private Image _Icon;
		private float _CompletionRatio;
		private float _UtilizationRatio;
		private float _UseRatio;
		private int _LinkGroup;
		/// <summary>
		/// Může obsahovat rozšiřující text, který bude vracet metoda ToolTip.
		/// </summary>
		public string TextNote;
		/// <summary>
		/// Může obsahovat pole stringů, od nejkratšího po nejkomplexnější, které slouží jako zdroj popisku pro element v době jeho editace.
		/// Pokud je null, pak jako popisek bude sloužit text řádku.
		/// Pokud není null, mělo by pole obsahovat popisky v různé délce, počínaje nejkratším až po nejdelší.
		/// Metoda this.Caption() si pak vybere jeden text podle vhodné délky, pro aktuální velikost grafu.
		/// </summary>
		public string[] TextCaptions;
        private CaptionVisibilityMode _CaptionVisibility = CaptionVisibilityMode.Default;
		#endregion
		#region POMOCNÉ METODY
		/// <summary>
		/// Defaultní vizuální styl objektu, který se běžně použije v konstruktoru. Odvozuje vizuální styl podle třídy.
		/// Ve vytvářeném objektu je vložen do this.ElementVisualType. Tam je možno styl upravit.
		/// </summary>
		protected GraphElementVisualType DefaultVisualType
		{
			get
			{	// Vizuální typ = barevnost.
				return GraphElementVisualType.Type1;
			}
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="layerType">Typ vrstvy elementu</param>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(GraphElementLayerType layerType, DataElementEditingMode editingMode, GraphElementVisualType visualType)
		{
			this._ElementLayer = layerType;
			this._ElementEditingMode = editingMode;
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="layerType">Typ vrstvy elementu</param>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		/// <param name="completionRatio">Poměr splnění gantu</param>
		/// <param name="utilizationRatio">Poměr využití kapacity</param>
		public void SetElementProperty(GraphElementLayerType layerType, DataElementEditingMode editingMode, GraphElementVisualType visualType, float completionRatio, float utilizationRatio)
		{
			SetElementProperty(layerType, editingMode, visualType);
			this._UtilizationRatio = utilizationRatio;
			this._CompletionRatio = completionRatio;
		}
		/// <summary>
		/// Nastaví zakotvení tohoto elementu do mapy okolních elementů.
		/// </summary>
		/// <param name="parentWorkGId">WorkGID parenta. Ten je jen jeden.</param>
		/// <param name="previousWorkGIds">GID[] předchozích WorkGID (ne elementy, ale tasky).</param>
		/// <param name="nextWorkGIds">GID[] návazných WorkGID (ne elementy, ale tasky).</param>
		/// <param name="firstChildWorkGIds">GID[] podřízených WorkGID (ne elementy, ale tasky).</param>
		public void SetElementNodes(GID parentWorkGId, GID[] previousWorkGIds, GID[] nextWorkGIds, GID[] firstChildWorkGIds)
		{
			this._ParentWorkGId = parentWorkGId;
			this._PreviousWorkGIds = previousWorkGIds;
			this._NextWorkGIds = nextWorkGIds;
			this._FirstChildWorkGIds = firstChildWorkGIds;
		}
		/// <summary>
		/// Uloží do své paměti texty, které se budou používat jako Caption při editaci elementu jako popisek v řádku.
		/// </summary>
		/// <param name="captions"></param>
		public void SetElementCaptions(params string[] captions)
		{
			this.TextCaptions = captions;
		}
		#endregion
	}
	#endregion
}
